//
//  Permutator.mm
//  FilePermutator
//
//  Created by Christopher Kemsley on 4/7/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include <vector>
#include <iostream>
#include "Permutator.h"



struct	PermutationArray
{
	
	std::vector<unsigned> const&	indexes () const
		{
		return mIndexes ;
		}
	std::vector<unsigned> const&	counts () const
		{
		return mCounts ;
		}
	
	PermutationArray	( PermutationArray const & copy )
	:	mIndexes ( copy.mIndexes ) , mCounts ( copy.mCounts ) { }
	
	PermutationArray	( Permutator * permutator )
	:	mIndexes () , mCounts ()
		{
		for ( PermutatorEntry * entry in permutator.entries )
			{
			unsigned count = entry.files.count ;
			if ( count == 0 )
				[NSException raise:NSInvalidArgumentException format:@"Permutator entry %@ has zero elements",entry] ;
			mCounts . push_back ( count ) ;
			mIndexes . push_back ( 0 ) ;
			}
		if ( mCounts.size() == 0 )
			[NSException raise:NSInvalidArgumentException format:@"Permutator %@ has zero elements",permutator] ;
		}
	
	~PermutationArray	()
		{ }
	
	void	printIndexSet () const
		{
		std::vector<unsigned>::const_iterator	here	=	mIndexes.begin() ;
		std::vector<unsigned>::const_iterator	lHere	=	mCounts.begin() ;
		std::vector<unsigned>::const_iterator	end		=	mIndexes.end() ;
		std::cout << "{ " ;
		while ( here != end )
			{
			unsigned 	value	=	*(here++) ;
			unsigned 	limit	=	*(lHere++) ;
			std::cout << "("<<value<<","<<limit<<") " ;
			}
		std::cout << "}\n" ;
		}
	bool	hasElements ()
		{
		//printIndexSet () ;
		return mIndexes.back() < mCounts.back() ;
		}
	void	increment ()
		{
		mIndexes[0] ++ ;
		
		std::vector<unsigned>::iterator	here	=	mIndexes.begin() ;
		std::vector<unsigned>::iterator	lHere	=	mCounts.begin() ;
		std::vector<unsigned>::iterator	end		=	mIndexes.end() - 1 ;
		while ( here != end )
			{
			unsigned &	value	=	*(here++) ;
			unsigned &	limit	=	*(lHere++) ;
			
			while ( value >= limit )
				{
				value = 0 ;
				( *here ) ++ ;
				}
			}
		}
	
	
private:
	std::vector<unsigned>	mIndexes ;
	std::vector<unsigned>	mCounts ;
} ;







@implementation PermutatorEnumerator {
	Permutator*			permutator ;
	PermutationArray*	enumerator ;
}
@synthesize permutator ;

- (id)	initWithPermutator:(Permutator*)inPermutator {
	if ( (self = [super init]) )
		{
		permutator = inPermutator ;
		enumerator = new PermutationArray ( inPermutator ) ;
		}
	return self ;
}
- (void)	dealloc {
	delete enumerator ;
}

- (NSString*)	description {
	return [ [ [super description] stringByAppendingFormat:@"{ permutator=%@ }",permutator ] stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n" ];
}

- (NSArray*)	allPermutations {
	NSMutableArray * all = [NSMutableArray arrayWithCapacity:200] ;
	
	NSArray * a = 0 ;
	while ( 0 != ( a = [self nextPermutation] ) )
		[all addObject:a] ;
	
	return all ;
}
- (NSArray*)	nextPermutation {
	if ( ! enumerator -> hasElements() ) return 0 ;
	
	PermutationArray &	a		=	*enumerator ;
	
	NSUInteger			i		=	0 ;
	
	NSMutableArray	*	files	=	[NSMutableArray arrayWithCapacity:permutator.entries.count] ;
	
	for ( PermutatorEntry * entry in permutator.entries )
		{
		PermutatorFile*	file	=	[ entry.files objectAtIndex:a.indexes()[i++] ] ;
		[files addObject:file] ;
		}
	
	
	enumerator -> increment() ;
	
	
	return files ;
}

@end
